﻿using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Internal.KuiHuaBaoDian.Services.Animation;
using UnityEngine;
using UnityEngine.Assertions;

namespace KuiHuaBaoDian.Services.Animation {

    /// <summary>
    /// 动画服务
    /// </summary>
    public static class AnimationService {

        /// <summary>
        /// 单例 
        /// </summary>
        public static IAnimationService Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IAnimationService>();
        private static IAnimationService m_Instance;
    }

    /// <summary>
    /// 动画服务
    /// </summary>
    public interface IAnimationService {

        /// <summary>
        /// 使参数<paramref name="handle"/>指示的动画句柄加入到动画服务中
        /// </summary>
        /// <param name="gameObject">动画控制的<see cref="UnityEngine.GameObject"/></param>
        /// <returns>动画句柄</returns>
        AnimationHandle Register(GameObject gameObject);

        /// <summary>
        /// 加载
        /// </summary>
        /// <param name="handle">动画句柄</param>
        /// <param name="scheme">动画方案</param>
        /// <returns>异步任务</returns>
        UniTask Load(AnimationHandle handle, IAnimationScheme scheme);

        /// <summary>
        /// 播放参数<paramref name="state"/>指示状态的动画剪辑
        /// </summary>
        /// <param name="handle">动画句柄，若提供值为<code>null</code>，则对所有加入动画系统中的动画执行</param>
        /// <param name="state">状态</param>
        /// <returns>异步任务</returns>
        UniTask Play(AnimationHandle handle, AnimationState state);

        /// <summary>
        /// 播放参数<paramref name="state"/>指示状态的覆盖后动画剪辑
        /// </summary>
        /// <param name="handle">动画句柄，若提供值为<code>null</code>，则对所有加入动画系统中的动画执行</param>
        /// <param name="state">状态</param>
        /// <param name="clipName">剪辑名称</param>
        /// <param name="assetKey">资产键</param>
        /// <returns>异步任务</returns>
        UniTask PlayOverride(AnimationHandle handle, AnimationState state, AnimationClipName clipName, string assetKey);

        /// <summary>
        /// 向控制器发送触发参数
        /// </summary>
        /// <param name="handle">动画句柄，若提供值为<code>null</code>，则对所有加入动画系统中的动画执行</param>
        /// <param name="trigger">触发</param>
        /// <returns>异步任务</returns>
        UniTask Trigger(AnimationHandle handle, AnimationTrigger trigger);

        /// <summary>
        /// 向控制器发送触发参数，且触发成功则播放覆盖后动画剪辑
        /// </summary>
        /// <param name="handle">动画句柄，若提供值为<code>null</code>，则对所有加入动画系统中的动画执行</param>
        /// <param name="trigger">触发</param>
        /// <param name="clipName">剪辑名称</param>
        /// <param name="assetKey">资产键</param>
        /// <returns>异步任务</returns>
        UniTask TriggerOverride(AnimationHandle handle, AnimationTrigger trigger, AnimationClipName clipName, string assetKey);

        /// <summary>
        /// 销毁
        /// </summary>
        /// <param name="handle">动画句柄</param>
        void Dispose(AnimationHandle handle);
    }

    [Implement(typeof(IAnimationService))]
    internal sealed class KHBD_AnimationService : IAnimationService {

        private readonly IDictionary<AnimationHandle, IAnimation> m_Dictionary = new Dictionary<AnimationHandle, IAnimation>();

        public AnimationHandle Register(GameObject gameObject) {
            Assert.IsNotNull(gameObject);

            var animation = KHBD.TypeMap.Instantiate<IAnimation>();
            (animation as IAnimationInternal).Initialize(gameObject);

            m_Dictionary.Add(animation.Handle, animation);
            return animation.Handle;
        }

        public async UniTask Load(AnimationHandle handle, IAnimationScheme scheme) {
            Assert.IsFalse(handle.Equals(AnimationHandle.None));
            Assert.IsNotNull(scheme);

            if (m_Dictionary.TryGetValue(handle, out IAnimation animation)) {
                await animation.Load(scheme);
            }
        }

        public UniTask Play(AnimationHandle handle, AnimationState state) {
            if (handle.Equals(AnimationHandle.None)) {
                var taskSet = new HashSet<UniTask>();
                foreach (var value in m_Dictionary.Values) {
                    taskSet.Add(value.Play(state));
                }
                return UniTask.WhenAll(taskSet);
            } else if (m_Dictionary.TryGetValue(handle, out var animation)) {
                return animation.Play(state);
            }
            return UniTask.CompletedTask;
        }

        public UniTask PlayOverride(AnimationHandle handle, AnimationState state, AnimationClipName clipName, string assetKey) {
            if (handle.Equals(AnimationHandle.None)) {
                var taskSet = new HashSet<UniTask>();
                foreach (var value in m_Dictionary.Values) {
                    taskSet.Add(value.PlayOverride(state, clipName, assetKey));
                }
                return UniTask.WhenAll(taskSet);
            } else if (m_Dictionary.TryGetValue(handle, out var animation)) {
                return animation.PlayOverride(state, clipName, assetKey);
            }
            return UniTask.CompletedTask;
        }

        public UniTask Trigger(AnimationHandle handle, AnimationTrigger trigger) {
            if (handle.Equals(AnimationHandle.None)) {
                var taskSet = new HashSet<UniTask>();
                foreach (var value in m_Dictionary.Values) {
                    taskSet.Add(value.Trigger(trigger));
                }
                return UniTask.WhenAll(taskSet);
            } else if (m_Dictionary.TryGetValue(handle, out var animation)) {
                return animation.Trigger(trigger);
            }
            return UniTask.CompletedTask;
        }

        public UniTask TriggerOverride(AnimationHandle handle, AnimationTrigger trigger, AnimationClipName clipName, string assetKey) {
            if (handle.Equals(AnimationHandle.None)) {
                var taskSet = new HashSet<UniTask>();
                foreach (var value in m_Dictionary.Values) {
                    taskSet.Add(value.TriggerOverride(trigger, clipName, assetKey));
                }
                return UniTask.WhenAll(taskSet);
            } else if (m_Dictionary.TryGetValue(handle, out var animation)) {
                return animation.TriggerOverride(trigger, clipName, assetKey);
            }
            return UniTask.CompletedTask;
        }

        public void Dispose(AnimationHandle handle) {
            Assert.IsFalse(handle.Equals(AnimationHandle.None));

            if (m_Dictionary.TryGetValue(handle, out IAnimation animation)) {
                m_Dictionary.Remove(handle);
                (animation as IDisposable).Dispose();
            }
        }
    }
}
